package com.hzw.saas.service.storage.service;

import java.math.BigDecimal;
import java.util.*;
import javax.annotation.Resource;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.storage.IUserSpaceService;
import com.hzw.saas.api.storage.bo.UserSpaceBO;
import com.hzw.saas.api.storage.enums.StorageSpaceEnum;
import com.hzw.saas.api.user.IUserRightsService;
import com.hzw.saas.api.user.bo.UserRightsBO;
import com.hzw.saas.api.user.constant.UserRightsConsts;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.config.util.MybatisPlusUtil;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.common.util.SnowFlakeUtil;
import com.hzw.saas.common.util.UnitConvertUtils;
import com.hzw.saas.common.util.enums.StorageUnitEnum;
import com.hzw.saas.service.storage.mapper.UserSpaceMapper;
import com.hzw.saas.service.storage.model.UserSpace;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zzl
 * @since 06/28/2021
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserSpaceServiceImpl extends ServiceImpl<UserSpaceMapper, UserSpace> implements IUserSpaceService, IService<UserSpace> {

    @Resource
    private IUserRightsService userRightsService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void initUserSpace(String userId) {
        Optional<UserRightsBO> userRights = Optional.ofNullable(userRightsService.getUserRightsByUser(userId));
        String cloudStorage = userRights.map(UserRightsBO::getCloudStorageSize).orElse(UserRightsConsts.DEFAULT_CLOUD_STORAGE_SIZE);
        Date expireTime = userRights.map(UserRightsBO::getCloudStorageExpireTime).orElse(null);
        StorageUnitEnum unitEnum = StorageUnitEnum.assertInstance(UnitConvertUtils.getStorageUnit(cloudStorage));

        List<String> storageSpaceList = StorageSpaceEnum.listCodes(StorageSpaceEnum.rootCode(), false, true);
        List<UserSpaceBO> userSpaceList = this.list(userId);
        BigDecimal totalValue = UnitConvertUtils.convertStorage(cloudStorage, StorageUnitEnum.BYTE);
        for (UserSpaceBO userSpaceBO : userSpaceList) {
            if (storageSpaceList.contains(userSpaceBO.getSpaceId())) {
                BigDecimal spaceValue = UnitConvertUtils.convertStorage(userSpaceBO.getSpaceSize(), StorageUnitEnum.BYTE);
                totalValue = NumberUtil.sub(totalValue, spaceValue);
                storageSpaceList.remove(userSpaceBO.getSpaceId());
            } else {
                super.lambdaUpdate().eq(UserSpace::getUserId, userId).eq(UserSpace::getSpaceId, userSpaceBO.getSpaceId()).remove();
            }
        }
        BigDecimal remainAverageValue = NumberUtil.div(totalValue, storageSpaceList.size());
        remainAverageValue = UnitConvertUtils.convertStorage(remainAverageValue.toString(), StorageUnitEnum.BYTE, unitEnum);
        String remainAverageSize = UnitConvertUtils.beautifyNumber(remainAverageValue, 2).concat(unitEnum.text());
        for (String spaceId : storageSpaceList) {
            this.add(userId, spaceId, remainAverageSize, expireTime);
        }
    }

    @Override
    public UserSpaceBO get(String userId, String spaceId) {
        if (StrUtil.hasBlank(userId, spaceId))
            return null;
        LambdaQueryChainWrapper<UserSpace> query = super.lambdaQuery().eq(UserSpace::getUserId, userId)
            .eq(UserSpace::getSpaceId, spaceId).orderByDesc(UserSpace::getUpdateTime);
        MybatisPlusUtil.limitOne(query);
        UserSpace one = query.one();
        return MapperUtil.nf().map(one, UserSpaceBO.class);
    }

    @Override
    public List<UserSpaceBO> list(String userId) {
        if (StrUtil.isBlank(userId))
            return new ArrayList<>();
        List<UserSpace> list = super.lambdaQuery()
            .eq(UserSpace::getUserId, userId).orderByAsc(UserSpace::getSpaceId).list();
        return MapperUtil.nf().mapAsList(list, UserSpaceBO.class);
    }

    @Override
    public void add(String userId, String spaceId, String spaceSize, Date expireTime) {
        this.checkIdParams(userId, spaceId);
        if (StrUtil.isBlank(spaceSize))
            spaceSize = UserRightsConsts.DEFAULT_CLOUD_STORAGE_SIZE;
        UserSpace entity = new UserSpace()
            .setUserId(userId)
            .setSpaceId(spaceId)
            .setSpaceSize(spaceSize)
            .setExpireTime(expireTime);
        entity.setPid(SnowFlakeUtil.nextIdStr());
        super.save(entity);
    }

    @Override
    public boolean update(String userId, String spaceId, String spaceSize, Date expireTime) {
        this.checkIdParams(userId, spaceId);
        return super.lambdaUpdate()
            .eq(UserSpace::getUserId, userId)
            .eq(UserSpace::getSpaceId, spaceId)
            .set(Objects.nonNull(spaceSize), UserSpace::getSpaceSize, spaceSize)
            .set(Objects.nonNull(expireTime), UserSpace::getExpireTime, expireTime)
            .update();
    }


    @Override
    public boolean delete(String userId, String spaceId) {
        this.checkIdParams(userId, spaceId);
        return super.lambdaUpdate().eq(UserSpace::getUserId, userId)
            .eq(UserSpace::getSpaceId, spaceId).remove();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchSave(String userId, String totalSize, Date expireTime) {
        if (StrUtil.isBlank(totalSize))
            return;
        List<String> spaceList = StorageSpaceEnum.listCodes(StorageSpaceEnum.rootCode(), false, true);
        String size = UnitConvertUtils.divStorage(totalSize, spaceList.size(), 4);
        // 清除可能的原始数据
        super.lambdaUpdate().eq(UserSpace::getUserId, userId).remove();
        for (String spaceId : spaceList) {
            this.add(userId, spaceId, size, expireTime);
        }
    }

    @Override
    public void batchUpdate(String userId, String totalSize, Date expireTime) {
        List<String> spaceList = StorageSpaceEnum.listCodes(StorageSpaceEnum.rootCode(), false, true);
        String size = null;
        if (StrUtil.isNotBlank(totalSize)) {
            size = UnitConvertUtils.divStorage(totalSize, spaceList.size(), 4);
        }
        for (String spaceId : spaceList) {
            this.update(userId, spaceId, size, expireTime);
        }
    }

    private void checkIdParams(String userId, String spaceId) {
        AssertUtil.assertThrow("userId 或 spaceId 为空", StrUtil.hasBlank(userId, spaceId));
    }
}
